home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 51 / Amiga Format CD51 (2000-03-10)(Future Publishing)(GB)[!][issue 2000-04].iso / -in_the_mag- / pdselect / blizkick / romupdatesplit.e < prev    next >
Text File  |  2000-02-16  |  14KB  |  540 lines

  1. -> FILE: ESrc:Own/romupdatesplit.e          REV: 4 --- split V44 rom update to executables
  2. /* History
  3.    0      Started in January 2000.
  4.    1      Quickly hacked up some ident code for BlizKick 1.20 beta.
  5.    2      15th Jan 2000: Added proper system detection. Added computer
  6.           specific switches. Use 'ALL' to emulate old way of operation.
  7.    3      Added CPU, FPU and NOBOARDCHECK switches.
  8.    4      18th Jan: Attnflags test was broken. Added support for some
  9.           future stuff. :)
  10. */
  11.  
  12.  
  13. /*
  14.   V44 SetPatch compatible "devs:AmigaOS ROM Update" file
  15.   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  16.   by Harry "Piru" Sintonen. This information is 100% incorrect, or at
  17.   least you must assume so. you have been warned. :)
  18.  
  19.   STRUCTURE romfileheader,0
  20.   ULONG  numexecutables    ; number of valid executables included
  21.   ULONG  file_1_offset
  22.   ULONG  file_2_offset
  23.   ULONG  file_numexecutables_offset
  24.   ; there is no SIZEOF
  25.  
  26.   STRUCTURE exewrapper,0
  27.   ULONG  ew_reqflags    ; flagset
  28.   ULONG  ew_attnflags   ; attn flags bits that must be set for this module
  29.   ULONG  ew_boardid     ; if bit 0 is set, (manufacturer<<16|product) board must be found
  30.   ULONG  ew_unused      ; must be 0
  31.   LABEL  executable
  32.   ; there is no SIZEOF
  33.  
  34.   ; flags as of now
  35.   BITDEF  REQ,OCS_DENISE1,0
  36.   BITDEF  REQ,OCS_DENISE2,1
  37.   BITDEF  REQ,ECS_DENISE1,2
  38.   BITDEF  REQ,ECS_DENISE2,3
  39.   BITDEF  REQ,A3000,4
  40.   BITDEF  REQ,A600,5
  41.   BITDEF  REQ,A1200,8
  42.   BITDEF  REQ,A4000,9
  43.   BITDEF  REQ,A4000T,10
  44.   BITDEF  REQ,CD32,11
  45.  
  46.     scsi.device ->
  47.  
  48.     a300.ld.strip           A1200 IDE      IDE_scsidisk
  49.     a600.ld.strip           A600 IDE       IDE_scsidisk
  50.     a1000.ld.strip          A4000[T] IDE   IDE_scsidisk
  51.     a4000t.ld.strip         A4000T SCSI    A4000T_scsidisk.devuce
  52.     scsidisk.ld.strip       A3000[T]       scsidisk
  53.  
  54.     name: FileSystem.resource -> FILESYSRES
  55.     filesystem -> FILESYSTEM
  56. */
  57.  
  58.  
  59. OPT REG=5,RTD,020
  60.  
  61. MODULE 'dos/dos','dos/doshunks','exec/memory','exec/execbase',
  62.        'graphics/gfxbase','exec/execbase'
  63.  
  64. MODULE 'expansion'
  65.  
  66. ENUM ARG_FROM,ARG_A600,ARG_A1200,ARG_A3000,ARG_A4000I,ARG_A4000S,
  67.      ARG_CD32,ARG_CPU,ARG_FPU,ARG_NOBOARDCHECK,ARG_ALL,ARG_TO,
  68.      NUMARGS
  69.  
  70. ENUM ER_OK,ER_PARAM,ER_INPUTFILE,ER_OUTPUTFILE,ER_READ,ER_SEEK,ER_WRITE,
  71.      ER_MEM,ER_CTRLC,ER_BADFILE
  72.  
  73.  
  74. ENUM UNKNOWN,ROMUPDIDENT,FILESYSTEM,FILESYSRES,RAMHANDLER,CONSOLEDEVICE,
  75.      UNKNOWNSCSI,UNKNOWNSCSIIDE,A300,A600,A1000,A4000,SCSIDISK,NUMOF
  76.  
  77. -> flags
  78. SET REQF_OCS_DENISE1,REQF_OCS_DENISE2,REQF_ECS_DENISE1,REQF_ECS_DENISE2,
  79.     REQF_A3000,REQF_A600,REQF_NONDEF6,REQF_NONDEF7,REQF_A1200,REQF_A4000,
  80.     REQF_A4000T,REQF_CD32
  81.  
  82.  
  83. DEF array[NUMARGS]:ARRAY OF LONG
  84. DEF ifh,ofh,buf[5]:ARRAY OF LONG
  85.  
  86. DEF counts[NUMOF]:ARRAY OF LONG
  87. DEF namebody[NUMOF]:ARRAY OF LONG
  88.  
  89. DEF attnflags=-1
  90.  
  91. PROC main() HANDLE
  92.   DEF rdargs=0,oname=0:PTR TO CHAR,ioerr,ret
  93.   DEF r,pname[64]:STRING
  94.   DEF lock=0,olddir,chdir=0,fib:PTR TO fileinfoblock
  95.   DEF cpu,fpu
  96.  
  97.   IF (KickVersion(37)=0) OR ((Int(Long(4)+296) AND AFF_68020)=0)
  98.     WriteF('Haha!\n'); RETURN 666
  99.   ENDIF
  100.  
  101.   r:='$VER: romupdatesplit 1.0.2 (18.1.00)'
  102.   FOR r:=0 TO NUMARGS-1; array[r]:=0; ENDFOR
  103.   IF (rdargs:=ReadArgs('FROM=FILE/A,A600/S,A1200/S,A3000/S,A4000I/S,A4000S/S,' +
  104.                        'CD32/S,CPU/K/N,FPU/K/N,NOBOARDCHECK/S,ALL/S,TO',
  105.                        array,NIL))=0 THEN Raise(ER_PARAM)
  106.  
  107.   IF array[ARG_TO]
  108.     IF (lock:=Lock(array[ARG_TO],ACCESS_READ))=NIL THEN Raise(ER_PARAM)
  109.  
  110.     IF (fib:=AllocDosObject(DOS_FIB,NIL))=NIL THEN Raise(ER_PARAM)
  111.     ret:=Examine(lock,fib)
  112.     IF ret THEN r:=fib.direntrytype
  113.     FreeDosObject(DOS_FIB,fib)
  114.     IF ret=0 THEN Raise(ER_PARAM)
  115.     IF r<0
  116.       PrintF('TO must be a directory.\n')
  117.       Raise(ER_PARAM)
  118.     ENDIF
  119.  
  120.     olddir:=CurrentDir(lock); chdir:=1
  121.   ENDIF
  122.  
  123.   IF array[ARG_CPU]
  124.     cpu:=Long(array[ARG_CPU])
  125.     SELECT cpu
  126.       CASE 68000;
  127.         /* no-op */
  128.       CASE 68010;
  129.         attnflags:=attnflags OR AFF_68010
  130.       CASE 68020;
  131.         attnflags:=attnflags OR AFF_68010 OR AFF_68020
  132.       CASE 68030;
  133.         attnflags:=attnflags OR AFF_68010 OR AFF_68020 OR AFF_68030
  134.       CASE 68040;
  135.         attnflags:=attnflags OR AFF_68010 OR AFF_68020 OR AFF_68030 OR AFF_68040
  136.       CASE 68060;
  137.         attnflags:=attnflags OR AFF_68010 OR AFF_68020 OR AFF_68030 OR AFF_68040 OR $80
  138.       DEFAULT;
  139.         PrintF('bad CPU number \d\n',cpu)
  140.         Raise(ER_PARAM)
  141.     ENDSELECT
  142.   ENDIF
  143.   IF array[ARG_FPU]
  144.     fpu:=Long(array[ARG_FPU])
  145.     SELECT fpu
  146.       CASE 68881;
  147.         attnflags:=attnflags OR AFF_68881
  148.       CASE 68882;
  149.         attnflags:=attnflags OR AFF_68881 OR AFF_68882
  150.       CASE 68040;
  151.         attnflags:=attnflags OR AFF_68881 OR AFF_68882 OR AFF_FPU40
  152.       CASE 68060;
  153.         -> there's no AFF_FPU060
  154.         attnflags:=attnflags OR AFF_68881 OR AFF_68882 OR AFF_FPU40
  155.       DEFAULT;
  156.         PrintF('bad FPU number \d\n',fpu)
  157.         Raise(ER_PARAM)
  158.     ENDSELECT
  159.   ENDIF
  160.  
  161.  
  162.   r:=0
  163.   IF array[ARG_A600] THEN r++
  164.   IF array[ARG_A1200] THEN r++
  165.   IF array[ARG_A3000] THEN r++
  166.   IF array[ARG_A4000I] THEN r++
  167.   IF array[ARG_A4000S] THEN r++
  168.   IF array[ARG_CD32] THEN r++
  169.  
  170.   IF r>1
  171.     PrintF('specify only one of A600, A1200, A3000, A4000I, A4000S or CD32\n')
  172.     Raise(ER_PARAM)
  173.   ENDIF
  174.  
  175.   IF array[ARG_ALL] AND r
  176.     PrintF('specify only ALL or specific machine type\n')
  177.     Raise(ER_PARAM)
  178.   ENDIF
  179.  
  180.   IF array[ARG_ALL] AND (array[ARG_CPU] OR array[ARG_FPU] OR
  181.                          array[ARG_NOBOARDCHECK])
  182.     PrintF('you must not use CPU, FPU and/or NOBOARDCHECK with ALL\n')
  183.     Raise(ER_PARAM)
  184.   ENDIF
  185.  
  186.  
  187.   split(array[ARG_FROM])
  188.  
  189. EXCEPT DO
  190.   IF (exception="NEW") OR (exception="MEM") THEN exception:=ER_MEM
  191.  
  192.   IF exception
  193.     IF ioerr:=IoErr()
  194.       GetProgramName(pname,StrMax(pname)-1); PrintFault(ioerr,pname)
  195.     ENDIF
  196.     PrintF('Error \d: \s\n',exception,
  197.            ListItem(['','argument error','could not open input file',
  198.                     'could not open output file','read error',
  199.                     'seek error','write error','no memory','break',
  200.                     'bad file format'
  201.                     ],exception))
  202.     ret:=RETURN_ERROR
  203.   ELSE
  204.     ret:=RETURN_OK
  205.   ENDIF
  206.  
  207.   IF chdir THEN CurrentDir(olddir)
  208.   IF lock THEN UnLock(lock)
  209.  
  210.   IF ifh THEN Close(ifh)
  211.   IF ofh THEN Close(ofh)
  212.   IF exception AND oname THEN DeleteFile(oname)
  213.  
  214.   IF rdargs THEN FreeArgs(rdargs)
  215. ENDPROC ret
  216.  
  217.  
  218. PROC extractthis(head:PTR TO LONG)
  219.   DEF cdui,card,a3000,a4000,ncrscsi,mlisa=0,hrdenise=0,
  220.       gfxb:PTR TO gfxbase,flags,mask=0,attnf,af,
  221.       execb:PTR TO execbase,lib,manu,prod,exp
  222.  
  223.   IF array[ARG_ALL] THEN RETURN TRUE
  224.  
  225.   -> flag test
  226.  
  227.   flags:=head[]
  228.  
  229.   IF flags AND $F3F
  230.  
  231.     gfxb:=gfxbase
  232.  
  233.     Forbid()
  234.     IF (cdui:=FindResident('cdui.library'))
  235.       IF (cdui<$f80000) OR (cdui>$ffffff)
  236.         cdui:=0
  237.       ENDIF
  238.     ENDIF
  239.     card:=FindResident('card.resource')
  240.     a3000:=FindResident('A3000 bonus')
  241.     a4000:=FindResident('A4000 bonus')
  242.     ncrscsi:=FindResident('NCR scsi.device')
  243.     Permit()
  244.     
  245.     IF (gfxb.chiprevbits0 AND GFXF_AA_MLISA)
  246.       mlisa:=1
  247.     ENDIF
  248.     IF (gfxb.chiprevbits0 AND GFXF_HR_DENISE)
  249.       hrdenise:=1
  250.     ENDIF
  251.  
  252.     IF a4000
  253.       IF ncrscsi
  254.         mask:=REQF_A4000T
  255.       ELSE
  256.         mask:=REQF_A4000
  257.       ENDIF
  258.     ELSE
  259.       IF a3000
  260.         mask:=REQF_A3000
  261.       ELSEIF cdui 
  262.         mask:=REQF_CD32
  263.       ELSEIF card
  264.         IF mlisa
  265.           mask:=REQF_A1200
  266.         ELSE
  267.           mask:=REQF_A600
  268.         ENDIF
  269.       ELSEIF hrdenise
  270.         mask:=REQF_ECS_DENISE1 OR REQF_ECS_DENISE2
  271.       ELSE
  272.         mask:=REQF_OCS_DENISE1 OR REQF_OCS_DENISE2
  273.       ENDIF
  274.     ENDIF
  275.  
  276.     IF array[ARG_A600]
  277.       mask:=REQF_A600
  278.     ELSEIF array[ARG_A1200]
  279.       mask:=REQF_A1200
  280.     ELSEIF array[ARG_A3000]
  281.       mask:=REQF_A3000
  282.     ELSEIF array[ARG_A4000I]
  283.       mask:=REQF_A4000
  284.     ELSEIF array[ARG_A4000S]
  285.       mask:=REQF_A4000T
  286.     ELSEIF array[ARG_CD32]
  287.       mask:=REQF_CD32
  288.     ENDIF
  289.     
  290.     IF (flags AND mask)=0 THEN RETURN FALSE
  291.   ENDIF
  292.  
  293.   -> attnflags test
  294.  
  295.   IF attnf:=head[2] AND $FFFF
  296.     execb:=execbase
  297.     IF attnflags<>-1
  298.       af:=attnflags
  299.     ELSE
  300.       IF attnf AND $80
  301.         -> 060 attnflag required... make sure
  302.         -> 68060.library gets loaded...
  303.         IF execb.attnflags AND AFF_68040
  304.           IF (lib:=OpenLibrary('68060.library',0))
  305.             CloseLibrary(lib)
  306.           ENDIF
  307.         ENDIF
  308.       ENDIF
  309.       af:=execb.attnflags
  310.     ENDIF
  311.  
  312.     IF (attnf AND af)=0 THEN RETURN FALSE
  313.  
  314.   ENDIF
  315.  
  316.  
  317.   -> manu/prod test
  318.   IF array[ARG_NOBOARDCHECK]=0
  319.     IF head[2] AND 1
  320.       exp:=0
  321.       IF (expansionbase:=OpenLibrary('expansion.library',37))
  322.         manu:=Shr(head[2],16) AND $0FFFF
  323.         prod:=head[2] AND $0FFFF
  324.         exp:=FindConfigDev(NIL,manu,prod)
  325.         CloseLibrary(expansionbase)
  326.       ENDIF
  327.       IF exp=0 THEN RETURN FALSE
  328.     ENDIF
  329.   ENDIF
  330.  
  331. ENDPROC TRUE
  332.  
  333.  
  334. PROC getfiletype(ifh,offs,head:PTR TO LONG)
  335.   DEF buf[2048]:ARRAY OF CHAR,r,wpt:PTR TO INT,
  336.       name,idstring,len,hunk0
  337.  
  338.   IF Seek(ifh,offs,OFFSET_BEGINNING)<0 THEN Raise(ER_SEEK)
  339.   len:=Read(ifh,buf,2047)
  340.   IF len<30 THEN Raise(ER_READ)
  341.   buf[2047]:=0; buf[len]:=0
  342.   IF Seek(ifh,offs,OFFSET_BEGINNING)<0 THEN Raise(ER_SEEK)
  343.  
  344.   IF Long(buf)<>HUNK_HEADER THEN Raise(ER_BADFILE)
  345.   IF Long(buf+4) THEN Raise(ER_BADFILE)
  346.  
  347.   hunk0:=buf+28+Shl(Long(buf+8),2); IF hunk0>=(buf+2048) THEN Raise(ER_BADFILE)
  348.  
  349.   len:=len-(hunk0-buf); IF len<26 THEN Raise(ER_BADFILE)
  350.  
  351.   wpt:=hunk0
  352.   FOR r:=0 TO Shr(len-26,1)
  353.     IF (wpt[]++=$4AFC)
  354.       IF Long(wpt)=(wpt-2-hunk0)
  355.         name:=hunk0+Long(wpt+12)
  356.         idstring:=hunk0+Long(wpt+16)
  357.         IF (name>buf) AND (name<(buf+2048)) AND
  358.            (idstring>buf) AND (idstring<(buf+2048))
  359.  
  360.           IF StrCmp(name,'AmigaOS ROM Update')
  361.             RETURN ROMUPDIDENT
  362.           ELSEIF StrCmp(name,'filesystem')
  363.             RETURN FILESYSTEM;
  364.           ELSEIF StrCmp(name,'FileSystem.resource')
  365.             RETURN FILESYSRES;
  366.           ELSEIF StrCmp(name,'ram-handler')
  367.             RETURN RAMHANDLER;
  368.           ELSEIF StrCmp(name,'console.device')
  369.             RETURN CONSOLEDEVICE;
  370.           ELSEIF StrCmp(name,'NCR scsi.device')
  371.             RETURN A4000;
  372.           ELSEIF StrCmp(name,'scsi.device')
  373.  
  374.             IF StrCmp(idstring,'scsidisk ',STRLEN)
  375.               RETURN SCSIDISK;
  376.             ELSEIF StrCmp(idstring,'IDE_scsidisk ',STRLEN)
  377.  
  378.               IF head[] AND REQF_A600
  379.                 RETURN A600;
  380.               ELSEIF head[] AND REQF_A1200
  381.                 RETURN A300;
  382.               ELSEIF head[] AND REQF_A4000
  383.                 RETURN A1000;
  384.               ENDIF
  385.  
  386.               RETURN UNKNOWNSCSIIDE;
  387.             ENDIF
  388.  
  389.             RETURN UNKNOWNSCSI;
  390.           ENDIF
  391.         ENDIF
  392.       ENDIF
  393.     ENDIF
  394.   ENDFOR
  395.  
  396. ENDPROC UNKNOWN
  397.  
  398. PROC split(name)
  399.   DEF fsize,num,seektable:PTR TO LONG,t,r,stack,
  400.       head[5]:ARRAY OF LONG,fname[32]:STRING,seg,size,
  401.       cnts[12]:STRING,type
  402.  
  403.   fsize:=FileLength(name)
  404.   IF fsize<20 THEN Raise(ER_BADFILE)
  405.  
  406.   IF (ifh:=Open(name,MODE_OLDFILE))=NIL THEN Raise(ER_INPUTFILE)
  407.  
  408.   IF Read(ifh,{num},4)<>4 THEN Raise(ER_READ)
  409.  
  410.   IF num=HUNK_HEADER
  411.     PrintF('this file is already single executable!\n')
  412.     Raise(ER_PARAM)
  413.   ENDIF
  414.  
  415.   IF num>$ffff THEN Raise(ER_BADFILE)
  416.  
  417.   t:=Shl(num,2)
  418.   seektable:=NewR(t+4)
  419.  
  420.   IF Read(ifh,seektable,t)<>t THEN Raise(ER_READ)
  421.  
  422.   FOR r:=0 TO num-1
  423.     IF CtrlC() THEN Raise(ER_CTRLC)
  424.     IF seektable[r] AND 3 THEN Raise(ER_BADFILE)
  425.     IF Seek(ifh,seektable[r],OFFSET_BEGINNING)<0 THEN Raise(ER_SEEK)
  426.     IF Read(ifh,head,20)<>20 THEN Raise(ER_READ)
  427.     IF (Int(head+4)<>0) OR (head[3]<>0) OR (head[4]<>HUNK_HEADER)
  428.       Raise(ER_BADFILE)
  429.     ENDIF
  430.   ENDFOR
  431.  
  432.   FOR r:=0 TO NUMOF-1; counts[r]:=0; ENDFOR
  433.  
  434.   namebody[UNKNOWN]:='unknown'
  435.   namebody[ROMUPDIDENT]:='romupdate.idtag'
  436.   namebody[FILESYSTEM]:='FastFileSystem'
  437.   namebody[FILESYSRES]:='FileSystem.resource'
  438.   namebody[RAMHANDLER]:='ram-handler'
  439.   namebody[CONSOLEDEVICE]:='console.device'
  440.   namebody[UNKNOWNSCSI]:='unknown.ld.strip'
  441.   namebody[UNKNOWNSCSIIDE]:='unknown_ide.ld.strip'
  442.  
  443.   IF (array[ARG_ALL]=0) OR (array[ARG_A600] OR array[ARG_A1200] OR
  444.      array[ARG_A3000] OR
  445.      array[ARG_A4000I] OR array[ARG_A4000S] OR array[ARG_CD32])
  446.  
  447.     namebody[A300]:='scsi.device'
  448.     namebody[A600]:='scsi.device'
  449.     namebody[A1000]:='scsi.device'
  450.     namebody[A4000]:='NCR scsi.device'
  451.     namebody[SCSIDISK]:='scsi.device'
  452.  
  453.   ELSE
  454.  
  455.     namebody[A300]:='a300.ld.strip'
  456.     namebody[A600]:='a600.ld.strip'
  457.     namebody[A1000]:='a1000.ld.strip'
  458.     namebody[A4000]:='a4000.ld.strip'
  459.     namebody[SCSIDISK]:='scsidisk.ld.strip'
  460.  
  461.   ENDIF
  462.  
  463.   FOR r:=0 TO num-1
  464.     IF CtrlC() THEN Raise(ER_CTRLC)
  465.     IF Seek(ifh,seektable[r],OFFSET_BEGINNING)<0 THEN Raise(ER_SEEK)
  466.     IF Read(ifh,head,16)<>16 THEN Raise(ER_READ)
  467.  
  468.     size:=-16-seektable[r]+IF r<(num-1) THEN seektable[r+1] ELSE fsize
  469.     PutLong({filesize},size)
  470.     stack:=4096
  471.     IF seg:=InternalLoadSeg(ifh,NIL,[{readfunc},
  472.                                      {allocfunc},
  473.                                      {freefunc}]:LONG,{stack})=NIL
  474.       Raise(ER_BADFILE)
  475.     ENDIF
  476.     InternalUnLoadSeg(seg,{freefunc})
  477.  
  478.     IF extractthis(head)
  479.  
  480.       type:=getfiletype(ifh,seektable[r]+16,head)
  481.  
  482.       StringF(cnts,'.\d',counts[type])
  483.       StringF(fname,'\s\s',namebody[type],IF counts[type] THEN cnts ELSE '')
  484.  
  485.       PrintF('\d[02]: flags $\h[04]  offset $\h[06]  len $\h[06]  "\s"\n',
  486.              r,head[],seektable[r]+16,size,fname)
  487.  
  488.       IF Seek(ifh,seektable[r]+16,OFFSET_BEGINNING)<0 THEN Raise(ER_SEEK)
  489.       IF (ofh:=Open(fname,MODE_NEWFILE))=NIL THEN Raise(ER_OUTPUTFILE)
  490.       readwrite(size)
  491.       Close(ofh); ofh:=0
  492.  
  493.       counts[type]:=counts[type]+1
  494.  
  495.     ENDIF
  496.  
  497.   ENDFOR
  498.  
  499.   RETURN
  500. filesize:
  501.   LONG 0
  502. readfunc:
  503.   LEA    filesize(PC),A0
  504.   TST.L  (A0)
  505.   BGE.B  readok
  506.   MOVEQ  #0,D0
  507.   RTS
  508. readok:
  509.   CMP.L  (A0),D3
  510.   BLS.B  readskip
  511.   MOVE.L (A0),D3
  512. readskip:
  513.   SUB.L  D3,(A0)
  514.   JMP    Read(A6)
  515.  
  516. allocfunc:
  517.   MOVEQ  #0,D1
  518.   JMP    AllocMem(A6)
  519.  
  520. freefunc:
  521.   JMP    FreeMem(A6)
  522. ENDPROC
  523.  
  524. PROC readwrite(l)
  525.   DEF len,buf[8192]:ARRAY OF CHAR,buflen=8192
  526.   IF (len:=l AND $1FFFFFFF)=0 THEN RETURN
  527.  
  528.   IF len<buflen THEN buflen:=len
  529.  
  530.   WHILE len
  531.     IF CtrlC() THEN Raise(ER_CTRLC)
  532.     IF (l:=Read(ifh,buf,buflen))<1 THEN Raise(ER_READ)
  533.     IF Write(ofh,buf,l)<>l THEN Raise(ER_WRITE)
  534.     len:=len-l
  535.     IF len<buflen THEN buflen:=len
  536.   ENDWHILE
  537. ENDPROC
  538.  
  539.  
  540.